#include <iostream>

using namespace std;

/*
 * 若子类中有属性开辟到堆区，父类指针在释放时无法调用到子类的析构代码   -> 将父类中的析构函数改为虚析构或纯虚析构
 * 虚析构和纯虚析构
 *      都可以解决父类指针释放子类对象
 *      都需要有具体的函数实现
 * 区别
 *      若为纯虚析构，则为抽象类，无法实例化对象
 * 虚析构语法
 *      virtual ~类名(){}
 * 纯虚析构语法
 *      virtual ~类名()=0;
 */

class Animal{
public:
    Animal(){
        cout << "animal构造函数" << endl;
    }
    virtual void speak() = 0;
//    virtual ~Animal(){  // 虚析构
//        cout << "animal析构函数" << endl;
//    }

    virtual ~Animal() = 0;  //纯虚析构函数必须实现
};

Animal::~Animal() {
    cout << "animal纯虚析构函数" << endl;
}

class Cat: public Animal{
public:
    string * c_Name;
    Cat(string name){
        cout<< "cat构造函数" <<endl;
        c_Name = new string(name);
    }
    void speak(){
        cout << "小猫在说话" << endl;
    }
    ~Cat(){
        cout << "cat析构函数" << endl;
        if(c_Name != NULL){
            delete c_Name;
            c_Name = NULL;
        }
    }
};

void doSpeak(Animal * animal) {
    animal->speak();
    // 父类指针在析构时，不会调用子类中析构函数，容易导致子类中的堆区属性释放不干净 -> 将父类析构改为虚析构或者纯虚析构
    delete animal;
}

void test01(){
    Cat * c = new Cat("Tom");
    doSpeak(c);
}

int main() {

    test01();

    return 0;
}
